#include "AreaIntegrator.hxx"
#include "GeoTiffReader.hxx"
#include "IPNGUtils.hxx"

AreaIntegrator::AreaIntegrator()
{
    _labelFileName[0] = '\0';
    _demFileName[0] = '\0';
    _orthoFileName[0] = '\0';

    _orthoImage = 0;
    _demImage = 0;
}

bool AreaIntegrator::computeAreas()
{
    if( _labels.size() < 1 )
    {
        fprintf(stderr,"No labels to compute area.");
        return false;
    }

    if(strlen(_labelFileName) <= 4)
    {
        fprintf(stderr,"Please specify a valid label file name.");
        return false;
    }

    if(strlen(_orthoFileName) <=4)
    {
        fprintf(stderr,"Please specify a valid orthographic projected file name.");
        return false;
    }

    if(strlen(_demFileName) <=4)
    {
        fprintf(stderr,"Please specify a valid DEM file name.");
        return false;
    }

    GeoTiffReader tiffReader;
    
    if( _demImage )
    {
        delete _demImage;
        _demImage = 0;
    }

    if( _orthoImage )
    {
        delete _orthoImage;
        _orthoImage = 0;
    }



    _demImage = new GeoTiffImage;
    tiffReader.setGeoTiffFileName(_demFileName);
    tiffReader.setGeoTiffImage(_demImage);
    tiffReader.readImage();

    _orthoImage = new GeoTiffImage;
    tiffReader.setGeoTiffFileName(_orthoFileName);
    tiffReader.setGeoTiffImage(_orthoImage);
    tiffReader.readImage();

    FILE* labelFile = fopen(_labelFileName, "rb");
   
    fseek(labelFile, 0L, SEEK_END);
    
    int lfSize = ftell(labelFile);

    fseek(labelFile, 0L, SEEK_SET);

    char* buffer = new char[lfSize];

    fread(buffer,lfSize,1,labelFile);

    fclose(labelFile);

    printf("Label File Size:%d\n", lfSize);

    cv::Mat ipngData;
    cv::Mat ipngPalette;

    imdecode_palette_png( buffer, lfSize, _labelImage, _labelPalette);

    delete [] buffer;

    computeLabelAreas();
}

void AreaIntegrator::computeLabelAreas()
{
    if( (_labelImage.cols != _orthoImage->getWidth()) || (_labelImage.rows != _orthoImage->getHeight()))
    {
        fprintf(stderr, "Dimensions of Ortho Image and Label Image are different.\n");
        return;
    }

    int iWidth = _orthoImage->getWidth();
    int iHeight = _orthoImage->getHeight();
    
    double* upperRowData = new double[iWidth];
    double* centerRowData = new double[iWidth];
    double* lowerRowData = new double[iWidth];

    for( int iy = 0; iy < iHeight; iy++)    
    {
        if( iy > 0 )
        {
            double* tmp = upperRowData;
            upperRowData = centerRowData;
            centerRowData = lowerRowData;
            lowerRowData = tmp;
        }

        computeDEMRows( iy, upperRowData, centerRowData, lowerRowData);

        // Compute Arears
        for( int ix = 0; ix < iWidth; ix++ )
        {
            int label = _labelImage.at<uchar>(iy, ix);

            if(_labels.count(label))
            {
                double pixelArea = computePixelArea(ix, iy, (const double*)upperRowData, (const double*)centerRowData, (const double*)lowerRowData);
                _labels[label].Area += pixelArea;
            }
        }
    }

    delete [] upperRowData;
    delete [] centerRowData;
    delete [] lowerRowData;
}

double AreaIntegrator::computePixelArea(const int ix, const int iy, const double* upperRow, const double* centerRow, const double* lowerRow)
{   
    return 0.0;
}

double AreaIntegrator::getDEMValue(float px, float py)
{
    int dWidth = _demImage->getWidth();
    int dHeight = _demImage->getHeight();

    if( px < 0.0 || px > (dWidth - 1.0) || py < 0.0 || py > (dHeight - 1.0))
    {
        printf("Error: Wrong dem location(%f,%f).\n", px, py);
        return 0.0;
    }
    printf("Get DEM Value:(%f,%f)=%f\n", px, py, 0.0);
    return 0.0;    
}

void AreaIntegrator::computeDEMRows(int rowIdx, double* upperRow, double* centerRow, double* lowerRow)
{
    int iWidth = _orthoImage->getWidth();
    int iHeight = _orthoImage->getHeight();
    // Get Geo Scales and Model control point
    double demSx = 0.0;
    double demSy = 0.0;
    double demSz = 0.0;

    double demMx = 0.0;
    double demMy = 0.0;
    double demMz = 0.0;

    _demImage->getGeoScale(demSx, demSy, demSz);
    _demImage->getGeoModelCoord(demMx, demMy, demMz);


    double orthoSx = 0.0;
    double orthoSy = 0.0;
    double orthoSz = 0.0;

    double orthoMx = 0.0;
    double orthoMy = 0.0;
    double orthoMz = 0.0;

    _orthoImage->getGeoScale(orthoSx, orthoSy, orthoSz);
    _orthoImage->getGeoModelCoord(orthoMx, orthoMy, orthoMz);


    if( rowIdx == 0 )
    {
        // Compute Current Row Data
        for(int ix = 0; ix < iWidth; ix ++)
        {
            float px = (orthoMx + ix * orthoSx - demMx) / demSx;
            float py = (orthoMy - rowIdx * orthoSy - demMy ) / (-demSy);

            double* ptr = centerRow + ix;
            *ptr = getDEMValue( px, py);

            py = (orthoMy - (rowIdx + 1) * orthoSy - demMy ) / (-demSy);
            ptr = lowerRow + ix;
            *ptr = getDEMValue( px, py);                
        }
    }
    else
    {
        if( rowIdx < iHeight -1)
        {
            // Compute Current Row Data
            for(int ix = 0; ix < iWidth; ix ++)
            {
                float px = (orthoMx + ix * orthoSx - demMx) / demSx;
                float py = (orthoMy - (rowIdx + 1) * orthoSy - demMy ) / (-demSy);

                double* ptr = lowerRow + ix;
                *ptr = getDEMValue( px, py);
            }
        }

    }    
}